package org.eclipse.gittab.ui.views;

import static org.eclipse.gittab.player.SimpleNotePlayer.play;

import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.gittab.fretboard.Fretboard;
import org.eclipse.gittab.fretboard.Fretboard.FretboardListener;
import org.eclipse.gittab.fretboard.draw2d.Draw2dFretboard;
import org.eclipse.gittab.gitTabDsl.Fret;
import org.eclipse.gittab.gitTabDsl.GitTabDslFactory;
import org.eclipse.gittab.gitTabDsl.GuitarSound;
import org.eclipse.gittab.gitTabDsl.Note;
import org.eclipse.gittab.gitTabDsl.TabModel;
import org.eclipse.gittab.gitTabDsl.Tuning;
import org.eclipse.gittab.player.SimpleNotePlayer;
import org.eclipse.gittab.player.Player.PlayerListener;
import org.eclipse.gittab.ui.viewer.FretboardViewer;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.ResourceUtil;
import org.eclipse.ui.part.ViewPart;

public class FretboardView extends ViewPart implements FretboardListener,
		PlayerListener {

	/**
	 * The ID of the view as specified by the extension.
	 */
	public static final String ID = "org.eclipse.gittab.ui.FretboardView";

	private Action leftyAction;

	private Action linkAction;

	/**
	 * Linking with editor.
	 */
	private boolean linking;

	private FretboardViewer fretboard;

	private Tuning lastTuning;

	private final ResourceSet resourceSet;

	/**
	 * The constructor.
	 */
	public FretboardView() {

		resourceSet = new ResourceSetImpl();
	}

	/**
	 * This is a callback that will allow us to create the viewer and initialize
	 * it.
	 */
	@Override
	public void createPartControl(Composite parent) {

		Fretboard fb = new Draw2dFretboard(); // TODO inject

		fretboard = new FretboardViewer(fb);
		fretboard.config().paintOn(parent);

		makeActions();
		hookContextMenu();
		contributeToActionBars();

		fretboard.getFretboard().addFretboardListener(this);
		getSite().getPage().addPartListener(partListener2);
	}

	@Override
	public void dispose() {
		super.dispose();
		fretboard.getFretboard().removeFretboardListener(this);
		getSite().getPage().removePartListener(partListener2);
	}

	private void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu");
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				FretboardView.this.fillContextMenu(manager);
			}
		});
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();

		fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
	}

	private void fillLocalPullDown(IMenuManager manager) {
	}

	private void fillContextMenu(IMenuManager manager) {
		manager.add(leftyAction);
		// Other plug-ins can contribute there actions here
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}

	private void fillLocalToolBar(IToolBarManager manager) {

		manager.add(linkAction);

		manager.add(leftyAction);
	}

	protected void toggleLinking(boolean checked) {

		this.linking = checked;
		if (checked)
			editorActivated(getSite().getPage().getActiveEditor());
	}

	/**
	 * Loads the EMF {@link Resource} by using the
	 * {@link IFile#getLocationURI()} mechanism.
	 * 
	 * @param editor
	 */
	protected void editorActivated(IEditorPart editor) {

		if (!linking || !getViewSite().getPage().isPartVisible(this))
			return;

		IEditorInput input = editor.getEditorInput();

		IFile file = ResourceUtil.getFile(input);
		if (file != null) {

			URI uri = URI.createURI(file.getLocationURI().toString());

			Resource resource = resourceSet.getResource(uri, true);

			TabModel tabModel = (TabModel) resource.getContents().get(0);
			paintFretboard(tabModel.getTuning());
		}
	}

	/**
	 * Paints the fretboard if a tuning is available and if the last drawn
	 * tuning differs from the to be drawn fretboard.
	 * 
	 * @param tabModel
	 */
	public void paintFretboard(Tuning tuning) {

		boolean doPaint = tuning != null;

		if (doPaint && lastTuning != null) {
			doPaint = !lastTuning.equals(tuning);
		}

		if (doPaint) {

			fretboard.config().tuneTo(tuning).apply();
			lastTuning = tuning;
		}
	}

	private final IPartListener2 partListener2 = new IPartListener2() {

		public void partActivated(IWorkbenchPartReference ref) {
			if (ref.getPart(true) instanceof IEditorPart)
				editorActivated(getViewSite().getPage().getActiveEditor());
		}

		public void partBroughtToTop(IWorkbenchPartReference ref) {
			if (ref.getPart(true) == FretboardView.this)
				editorActivated(getViewSite().getPage().getActiveEditor());
		}

		public void partClosed(IWorkbenchPartReference ref) {
		}

		public void partDeactivated(IWorkbenchPartReference ref) {
		}

		public void partOpened(IWorkbenchPartReference ref) {
			if (ref.getPart(true) == FretboardView.this) {
				editorActivated(getViewSite().getPage().getActiveEditor());
			}
		}

		public void partHidden(IWorkbenchPartReference ref) {
		}

		public void partVisible(IWorkbenchPartReference ref) {
			if (ref.getPart(true) == FretboardView.this)
				editorActivated(getViewSite().getPage().getActiveEditor());
		}

		public void partInputChanged(IWorkbenchPartReference ref) {
		}
	};

	private void makeActions() {

		linkAction = new Action("Link with Editor", IAction.AS_CHECK_BOX) {
			@Override
			public void run() {
				toggleLinking(isChecked());
			}
		};
		linkAction.setImageDescriptor(PlatformUI.getWorkbench()
				.getSharedImages().getImageDescriptor(
						ISharedImages.IMG_TOOL_FORWARD));

		leftyAction = new Action("Lefty", IAction.AS_CHECK_BOX) {
			@Override
			public void run() {
				toggleLefty(isChecked());
			}
		};
		leftyAction.setImageDescriptor(PlatformUI.getWorkbench()
				.getSharedImages().getImageDescriptor(
						ISharedImages.IMG_OBJS_INFO_TSK));
	}

	protected void toggleLefty(boolean checked) {

		if (checked)
			fretboard.config().lefty().apply();
		else
			fretboard.config().standard().apply();
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	@Override
	public void setFocus() {
	}

	/**
	 * Plays the note on the {@link SimpleNotePlayer}.
	 */
	@Override
	public void clickedOn(int fret, int string) {

		final Note note = GitTabDslFactory.eINSTANCE.createNote();
		Fret gfret = GitTabDslFactory.eINSTANCE.createFret();

		gfret.setFret(fret);

		note.setGitString(lastTuning.getStrings().get(string - 1));
		note.setNote(gfret);

		new Thread(new Runnable() {

			@Override
			public void run() {
				// play the note
				play(note).use(lastTuning).seconds(2).with(GuitarSound.CLEAN);
			}
		}).start();
	}

	@Override
	public void noteOn(final List<Note> notes) {

		this.getSite().getShell().getDisplay().asyncExec(new Runnable() {

			@Override
			public void run() {
				fretboard.play(notes);
			}
		});

	}

	@Override
	public void noteOff(final List<Note> notes) {

		this.getSite().getShell().getDisplay().asyncExec(new Runnable() {

			@Override
			public void run() {
				fretboard.clear(notes);
			}
		});
	}
}